home *** CD-ROM | disk | FTP | other *** search
- // IconWell.m
- //
- // Free software created 1 Feb 1992
- // by Paul Burchard <burchard@math.utah.edu>.
-
- #import "IconWell.h"
- #import "IconWellControl.h"
- #import <appkit/appkit.h>
- #import <objc/Storage.h>
-
-
- @implementation IconWell
-
- static id windowList;
- static id wellsByWindow;
-
- + initialize
- {
- if(self == [IconWell class])
- {
- windowList = [[List alloc] initCount:0];
- wellsByWindow = [[List alloc] initCount:0];
- }
- return self;
- }
-
- + wellListFor:aWindow
- {
- int index;
-
- index = [windowList indexOf:aWindow];
- if(index == NX_NOT_IN_LIST) return nil;
- return [wellsByWindow objectAt:index];
- }
-
- - initFrame:(const NXRect *)frameRect
- {
- id theCell;
-
- [super initFrame:frameRect];
-
- theCell = [[ActionCell alloc] initIconCell:"Blank.tiff"];
- [theCell setBezeled:YES];
- [self setCell:theCell];
-
- iconPath = [[Storage alloc] initCount:0 elementSize:sizeof(char) description:"c"];
- sprintf(iconName, "IconWell-%ld", (long)[self self]);
-
- isHoldOnDrag = YES;
- [self setDraggable:YES droppable:YES];
- return self;
- }
-
- - free
- {
- int index;
- id list;
-
- [iconPath free];
- if(list = [IconWell wellListFor:window])
- {
- [list removeObject:self];
- if([list count] <= 0)
- {
- index = [wellsByWindow indexOf:list];
- [wellsByWindow removeObjectAt:index];
- [windowList removeObjectAt:index];
- [list free];
- }
- }
- return [super free];
- }
-
- - windowChanged:newWindow
- {
- id list;
-
- // Enter new well into global list.
- if(window || !newWindow) return nil;//!!!
- if(!(list = [IconWell wellListFor:newWindow]))
- {
- list = [[List alloc] initCount:0];
- [windowList addObject:newWindow];
- [wellsByWindow addObject:list];
- }
- [list addObjectIfAbsent:self];
- return self;
- }
-
- - setBezeled:(BOOL)flag
- {
- return [cell setBezeled:flag];
- }
-
- - (BOOL)isBezeled
- {
- return [cell isBezeled];
- }
-
- - setBordered:(BOOL)flag
- {
- return [cell setBordered:flag];
- }
-
- - (BOOL)isBordered
- {
- return [cell isBordered];
- }
-
- - setDraggable:(BOOL)dragFlag droppable:(BOOL)dropFlag
- {
- isDraggable = dragFlag;
- isDroppable = dropFlag;
- return self;
- }
-
- - (BOOL)isDraggable
- {
- return isDraggable;
- }
-
- - (BOOL)isDroppable
- {
- return isDroppable;
- }
-
- - setHoldOnDrag:(BOOL)flag
- {
- if(isDragging) return nil;
- isHoldOnDrag = flag;
- return self;
- }
-
- - (BOOL)isHoldOnDrag
- {
- return isHoldOnDrag;
- }
-
-
- - clear:sender
- {
- // This does not de-alloc Blank.tiff since it was not renamed.
- // This only affects this IconWell because of the unique iconName.
- [cell setIcon:"Blank.tiff"];
- [[NXImage findImageNamed:iconName] free];
- [iconPath setNumSlots:0];
- return self;
- }
-
- - (const char *)stringValue
- {
- if([iconPath count] <= 0) return 0;
- return (const char *)[iconPath elementAt:0];
- }
-
- - getTiffForPath:(const char *)pathString
- {
- int ok, length;
- char *tiff, *fakePath, *q;
- const char *p;
- NXStream *imageStream;
- id iconImage;
-
- // Ask WorkSpace for correct TIFF corresponding to path list pathString.
- // Since ``full paths'' are required, prepend '/' to each name if missing.
- if(!pathString) return nil;
- if(!(fakePath = (char *)malloc((strlen(pathString)+1)*sizeof(char))))
- return nil;
- for(p=pathString, q=fakePath; *p; p++)
- {
- if(p==pathString && *p!='/') *q++ = '/';
- *q++ = *p;
- if(*p=='\t' && *(p+1)!='/') *q++ = '/';
- }
- *q = 0;
- [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
- [[NXApp appSpeaker] getFileIconFor:fakePath
- TIFF:&tiff TIFFLength:&length ok:&ok];
- free(fakePath);
-
- // If no icon, use generic ".txt" icon.
- if(!ok)
- {
- [[NXApp appSpeaker]
- setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
- [[NXApp appSpeaker] getFileIconFor:"/file.txt"
- TIFF:&tiff TIFFLength:&length ok:&ok];
- if(!ok) return nil;
- }
-
- // Create NXImage from TIFF data.
- imageStream = NXOpenMemory(tiff, length, NX_READONLY);
- if(!imageStream) return nil;
- iconImage = [[NXImage alloc] initFromStream:imageStream];
- NXClose(imageStream);
- return iconImage;
- }
-
- - setStringValue:(const char *)aString
- {
- id iconImage;
-
- // If path is NULL, clear well.
- if(!aString) [self clear:self];
-
- // Ask WorkSpace for correct TIFF corresponding to path aString.
- if(!(iconImage = [self getTiffForPath:aString])) return nil;
-
- // Enter NXImage into cell, freeing previous image.
- // (Note: Common "Blank.tiff" image is not freed as it was never renamed.)
- [cell setIcon:"Blank.tiff"];
- [[NXImage findImageNamed:iconName] free];
- [iconImage setName:iconName];
- [cell setIcon:iconName];
-
- // Enter new path into iconPath, notify target.
- [iconPath setNumSlots:(strlen(aString)+1)];
- strcpy((char *)[iconPath elementAt:0], aString);
- [self sendAction:[cell action] to:[cell target]];
- return self;
- }
-
- - takeStringValueFrom:sender
- {
- id oldTarget = nil;
- id rtn;
-
- // If sender is target, don't send action (to avoid circularity).
- if(sender == [self target])
- { oldTarget = [self target]; [self setTarget:nil]; }
- rtn = [self setStringValue:[sender stringValue]];
- if(oldTarget) [self setTarget:oldTarget];
- return rtn;
- }
-
-
- - (BOOL)acceptsFirstMouse
- {
- return YES;
- }
-
- - (int)openFile:(const char *)fullPath ok:(int *)flag
- {
- int rtn;
-
- // Ask WorkSpace to open the file. No fudging paths now.
- [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
- rtn = [[NXApp appSpeaker] openFile:fullPath ok:flag];
- if(rtn != 0) return(rtn);
- return 0;
- }
-
- - (int)prepFile:(const char *)fullPath ok:(int *)flag
- {
- // Default is no prep needed on drag-out.
- return 0;
- }
-
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
- - delegate
- {
- return delegate;
- }
-
- - mouseDown:(NXEvent *)theEvent
- {
- int ok;
- char *fileName, *nxt;
- NXPoint mousePoint;
- NXRect iconRect;
- id success, handler;
-
- if(theEvent->data.mouse.click < 2)
- {
- // Single click means drag icon out.
-
- // Check if mouse actually clicked on icon.
- mousePoint = theEvent->location;
- [super convertPoint:&mousePoint fromView:nil];
- [self getBounds:&iconRect];
- [cell getIconRect:&iconRect];
- NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
- if(!NXMouseInRect(&mousePoint, &iconRect, [self isFlipped]))
- return self;
-
- // Don't actually start the drag until mouse gets dragged a bit.
- if(isDraggable)
- {
- isDragging = YES;
- [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
- dragFromEvent = *theEvent;
- }
- return self;
- }
- else if(theEvent->data.mouse.click == 2)
- {
- // Double-click means open file(s).
-
- // Delegate this task if possible.
- if([delegate respondsTo:@selector(openFile:ok:)]) handler = delegate;
- else handler = self;
-
- // Open files one by one.
- if([iconPath count] <= 0) return nil;
- fileName = (char *)[iconPath elementAt:0];
- nxt = strchr(fileName, '\t');
- success = self;
- while(fileName)
- {
- if(nxt) *nxt = 0;
- [handler openFile:fileName ok:&ok];
- if(!ok) success = nil;
- if(nxt)
- {
- *nxt = '\t';
- fileName = nxt+1;
- nxt = strchr(fileName, '\t');
- }
- else fileName = nxt = 0;
- }
- return success;
- }
- else
- {
- // Ignore higher multi-clicks.
- return self;
- }
- }
-
- - mouseDragged:(NXEvent *)theEvent
- {
- NXRect iconRect;
- int ok;
- char *fileName, *nxt;
- id success, handler;
-
- // Check if valid drag.
- if(!(isDragging && isDraggable && [cell icon] && [iconPath count]>0))
- { isDragging = NO; return self; }
-
- // Prep file(s) for drag; delegate this task if possible.
- if([delegate respondsTo:@selector(prepFile:ok:)]) handler = delegate;
- else handler = self;
- if([iconPath count] <= 0) return nil;
- fileName = (char *)[iconPath elementAt:0];
- nxt = strchr(fileName, '\t');
- success = self;
- while(fileName)
- {
- if(nxt) *nxt = 0;
- [handler prepFile:fileName ok:&ok];
- if(!ok) success = nil;
- if(nxt)
- {
- *nxt = '\t';
- fileName = nxt+1;
- nxt = strchr(fileName, '\t');
- }
- else fileName = nxt = 0;
- }
- if(!success) { isDragging = NO; return success; }
-
- // Try to drag icon out.
- [self getBounds:&iconRect];
- [cell getIconRect:&iconRect];
- NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
- success = [super dragFile:(const char *)[iconPath elementAt:0]
- fromRect:&iconRect slideBack:YES event:&dragFromEvent];
- isDragging = NO;
-
- // Take care of no-hold-on-drag.
- if(!isHoldOnDrag)
- {
- // Successful drag: clear path and icon to initial state.
- if(success) [self clear:self];
- // Unsuccessful drag: redisplay old icon in case it left.
- else [cell setIcon:iconName];
- }
- return success;
- }
-
- - mouseUp:(NXEvent *)theEvent
- {
- isDragging = NO;
- return self;
- }
-
- - (BOOL)isScreenPointInView:(double)x :(double)y
- {
- NXPoint point;
- NXRect rect;
-
- // If (x,y) is not in this IconWell, try next one in chain.
- point.x = x; point.y = y;
- [window convertScreenToBase:&point];
- [self convertPoint:&point fromView:nil];
- [self getBounds:&rect];
- if(NXMouseInRect(&point, &rect, [self isFlipped])) return YES;
- else return NO;
- }
-
- - (int)iconEntered:(int)windowNum at:(double)x :(double)y
- iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
- iconWidth:(double)iconWidth iconHeight:(double)iconHeight
- pathList:(char *)pathList
- {
- // Temporarily display new image if (x,y) is in this IconWell.
- if(!isDroppable || ![self isScreenPointInView:x :y])
- { isDropping = NO; return 0; }
- [cell setIcon:"Blank.tiff"];
- [cell setIcon:newIconName];
- isDropping = YES; return 0;
- }
-
- - (int)iconMovedTo:(double)x :(double)y
- {
- // Check if (x,y) moved in or out of this IconWell.
- if(!isDroppable) return 0;
- else if(![self isScreenPointInView:x :y])
- {
- if(!isDropping) return 0;
-
- // Mouse left; restore old image (unless no-hold-on-drag thing).
- [cell setIcon:"Blank.tiff"];
- if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
- isDropping = NO; return 0;
- }
- else if(!isDropping)
- {
- // Mouse entered; display new image.
- [cell setIcon:"Blank.tiff"];
- [cell setIcon:newIconName];
- isDropping = YES; return 0;
- }
- return 0;
- }
-
- - (int)iconExitedAt:(double)x :(double)y
- {
- if(!isDroppable || !isDropping) return 0;
-
- // Mouse left; restore old image (unless no-hold-on-drag thing).
- [cell setIcon:"Blank.tiff"];
- if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
- isDropping = NO; return 0;
- }
-
- - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
- {
- const char *path;
-
- // Check if (x,y) moved in or out of this IconWell.
- *flag = 0;
- if(!isDroppable) return 0;
- else if(![self isScreenPointInView:x :y]
- || !(path = [[IconWellControl controlFor:window] newIconPath]))
- {
- if(!isDropping) return 0;
-
- // Mouse left; restore old image (unless no-hold-on-drag thing).
- [cell setIcon:"Blank.tiff"];
- if(!isDragging || isHoldOnDrag) [cell setIcon:iconName];
- isDropping = NO; return 0;
- }
- else if(!isDropping)
- {
- // Mouse entered; display new image.
- [cell setIcon:"Blank.tiff"];
- [cell setIcon:newIconName];
- }
-
- // Replace path and icon with new ones; notify target.
- [iconPath setNumSlots:(strlen(path)+1)];
- strcpy((char *)[iconPath elementAt:0], path);
- [cell setIcon:"Blank.tiff"];
- [[NXImage findImageNamed:iconName] free];
- [[NXImage findImageNamed:newIconName] setName:iconName];
- [cell setIcon:iconName];
- [self sendAction:[cell action] to:[cell target]];
-
- // Accept icon and end drop.
- isDropping = NO;
- *flag = 1;
- return 0;
- }
-
- @end
-